#ifndef WIN_RAW_SOUND_OSTREAM_H
#define WIN_RAW_SOUND_OSTREAM_H


#include <Windows.h>
#include <ostream>
#include <queue>
#include "chronometer.h"


namespace hard {

class win_raw_sound_ostream
{
public:
  struct chunk//: public WAVEHDR
  {
    WAVEHDR* header;
    HWAVEOUT hWaveOut;
    mutable bool unprepare_;

    chunk(HWAVEOUT hWave, LPSTR block, DWORD size, bool doUnprepare = false): hWaveOut(hWave), unprepare_(doUnprepare)
    {
      init(hWaveOut, block, size);
    }

    chunk(const chunk& other)
    {
      memcpy(this,&other,sizeof(chunk));
      if(unprepare_)
        other.unprepare_=false;
    }

    void init(HWAVEOUT hWaveOut, LPSTR block, DWORD size);

    ~chunk();

    chunk& unprepare()
    {
      unprepare_=true;
      return *this;
    }
  };

private:
  WAVEFORMATEX wfx; /* look this up in your documentation */
  MMRESULT result;/* for waveOut return values */
  MMRESULT openResult;
  std::queue<chunk> chunks;
  std::queue<chunk> written;
  bool end;
  unsigned int threadId;
  chronometer chr;
  
  // resources
  HWAVEOUT hWaveOut; /* device handle */
  CRITICAL_SECTION csWriting;
  HANDLE thread;
  HANDLE endThread;
  HANDLE writeEvent;
  
  //friend void CALLBACK waveOutProc(HWAVEOUT hWaveOut, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2);
  friend unsigned int WINAPI win_raw_sound_ostream_thread(void* arg);
  void close();
  MMRESULT write_next();

public:

  static const MMRESULT QUEUE_EMPTY = 666001;

  win_raw_sound_ostream(DWORD nSamplesPerSec=44100, WORD wBitsPerSample=16, WORD nChannels=2);
  ~win_raw_sound_ostream()
  {
    close();
  }

  MMRESULT get_open_result() const
  {
    return openResult;
  }

  MMRESULT get_write_result() const
  {
    return result;
  }

  bool is_open() const
  {
    return openResult==MMSYSERR_NOERROR;
  }

  void clean_written();

  win_raw_sound_ostream& win_raw_sound_ostream::write(const void* s , std::streamsize n);


  chronometer get_chronometer() const
  {
    return chr;
  }
};

}
#endif


